﻿using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Operations;
using CID.Tests.Support;
using NUnit.Framework;
using Rhino.Mocks;

namespace CID.Domain.Tests.Operations
{
    [TestFixture]
    class ServerTests : BaseFunctionalTests
    {
        private const string Name = "Server Name";
        private const string Address = "Server Address";

        private Server server;

        public override void SetUp()
        {
            base.SetUp();
            server = new Server(Name, Address, StubsBuilder.BuildLocationStub());
        }

        #region Constructor
        [Test]
        public void CreateNewServer()
        {
            var location = StubsBuilder.BuildLocationStub();
            var server = new Server(Name, Address, location);

            Assert.AreEqual(Name, server.Name);
            Assert.AreEqual(Address, server.Address);
            Assert.AreEqual(location, server.Location);
            Assert.IsTrue(server.Enabled);
            CollectionAssert.IsEmpty(server.Assignments);
            CollectionAssert.IsEmpty(server.Roles);
            CollectionAssert.IsEmpty(server.Environments);
        }
        #endregion

        #region Roles
        [Test]
        public void GetRoles_OnlyOneAssignment_ShouldReturnOneRole()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);

            Assert.AreEqual(1, server.Roles.Count());
            CollectionAssert.Contains(server.Roles, serverRole);
        }

        [Test]
        public void GetRoles_TwoAssignmentsForSameRole_ShouldReturnOneRole()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            var anotherEnvironment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);
            server.Assign(anotherEnvironment, serverRole);

            Assert.AreEqual(1, server.Roles.Count());
            CollectionAssert.Contains(server.Roles, serverRole);
        }

        [Test]
        public void GetRoles_TwoAssignmentsForDifferentRoles_ShouldReturnTwoRoles()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            var anotherEnvironment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);
            server.Assign(anotherEnvironment, anotherServerRole);

            Assert.AreEqual(2, server.Roles.Count());
            CollectionAssert.Contains(server.Roles, serverRole);
            CollectionAssert.Contains(server.Roles, anotherServerRole);
        }
        #endregion

        #region Roles
        [Test]
        public void GetEnvironments_OnlyOneAssignment_ShouldReturnOneEnvironment()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);

            Assert.AreEqual(1, server.Environments.Count());
            CollectionAssert.Contains(server.Environments, environment);
        }

        [Test]
        public void GetEnvironments_TwoAssignmentsForSameEnvironment_ShouldReturnOneEnvironment()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub(); 
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);
            server.Assign(environment, anotherServerRole);

            Assert.AreEqual(1, server.Environments.Count());
            CollectionAssert.Contains(server.Environments, environment);
        }

        [Test]
        public void GetEnvironments_TwoAssignmentsForDifferentEnvironments_ShouldReturnTwoEnvironments()
        {
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            var environment = StubsBuilder.BuildEnvironmentStub();
            var anotherEnvironment = StubsBuilder.BuildEnvironmentStub();
            server.Assign(environment, serverRole);
            server.Assign(anotherEnvironment, anotherServerRole);

            Assert.AreEqual(2, server.Environments.Count());
            CollectionAssert.Contains(server.Environments, environment);
            CollectionAssert.Contains(server.Environments, anotherEnvironment);
        }
        #endregion

        #region Assign
        [Test]
        public void Assign_NoPreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();

            server.Assign(environment, serverRole);

            var assignment = server.Assignments.FirstOrDefault(x => (x.Environment.Equals(environment) && x.ServerRole.Equals(serverRole)));
            Assert.IsNotNull(assignment);
            Assert.AreEqual(1, server.Assignments.Count());
            environment.AssertWasCalled(x => x.AddAssignment(assignment));
            serverRole.AssertWasCalled(x => x.AddAssignment(assignment));
        }

        [Test, ExpectedException(typeof(DuplicateAssignmentException))]
        public void Assign_DuplicateAssignment_ShouldThrowException()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            server.AddAssignment(assignment);

            server.Assign(environment, serverRole);
        }
        #endregion

        #region Unassign
        [Test]
        public void Unassign_AnotherPreviousAssignment_NothingShouldHappen()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var anotherEnvironmnet = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            server.Assign(anotherEnvironmnet, serverRole);
            var assignment = server.Assignments.FirstOrDefault();

            server.Unassign(environment, serverRole);

            Assert.AreEqual(1, server.Assignments.Count());
            anotherEnvironmnet.AssertWasNotCalled(x => x.RemoveAssignment(assignment));
            serverRole.AssertWasNotCalled(x => x.RemoveAssignment(assignment));
        }

        [Test]
        public void Unassign_PreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            server.Assign(environment, serverRole);
            var assignment = server.Assignments.FirstOrDefault();

            server.Unassign(environment, serverRole);

            Assert.AreEqual(0, server.Assignments.Count());
            environment.AssertWasCalled(x => x.RemoveAssignment(assignment));
            serverRole.AssertWasCalled(x => x.RemoveAssignment(assignment));
        }
        #endregion

        #region AddAssignment
        [Test]
        public void AddAssignment_NoPreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            server.AddAssignment(assignment);

            var retrievedAssignment = server.Assignments.FirstOrDefault(x => (x.Environment.Equals(environment) && x.ServerRole.Equals(serverRole)));
            Assert.IsNotNull(retrievedAssignment);
            Assert.AreEqual(1, server.Assignments.Count());
        }

        [Test, ExpectedException(typeof(InvalidAssignmentException))]
        public void AddAssignment_AssignmentForAnotherEnvironment_ShouldThrowException()
        {
            var anotherServer = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(server: anotherServer);

            server.AddAssignment(assignment);
        }

        [Test, ExpectedException(typeof(DuplicateAssignmentException))]
        public void AddAssignment_DuplicateAssignment_ShouldThrowException()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            var duplicateAssignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            server.AddAssignment(assignment);
            server.AddAssignment(duplicateAssignment);
        }
        #endregion

        #region RemoveAssignment
        [Test]
        public void RemoveAssignment_NoPreviousAssignment_NothingShouldHappen()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            server.RemoveAssignment(assignment);

            Assert.AreEqual(0, server.Assignments.Count());
        }

        [Test]
        public void RemoveAssignment_AssignmentPreviouslyAdded_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var serverRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            server.AddAssignment(assignment);

            server.RemoveAssignment(assignment);

            Assert.AreEqual(0, server.Assignments.Count());
        }

        [Test, ExpectedException(typeof(InvalidAssignmentException))]
        public void RemoveAssignment_AssignmentForAnotherEnvironment_ShouldThrowException()
        {
            var anotherServer = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(server: anotherServer);

            server.RemoveAssignment(assignment);
        }
        #endregion

    }
}
